﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

namespace Utility.Serializable
{
	[Serializable]
	public struct NullableInt
	{
		[SerializeField] bool hasValue;
		[SerializeField] int value;

		public bool HasValue => hasValue;
		public int Value
		{
			get
			{
				if (hasValue)
				{
					return value;
				}
				throw new InvalidOperationException ("Value is null");
			}
		}

		//ctor
		public NullableInt (int value)
		{
			hasValue = true;
			this.value = value;
		}

		//conversion
		public static implicit operator NullableInt (int value)
		{
			return new NullableInt (value);
		}

		public static explicit operator int (NullableInt nullable)
		{
			return nullable.Value;
		}

		public static implicit operator int? (NullableInt nullable)
		{
			return nullable.hasValue ? new int? (nullable.value) : default;
		}

		public static implicit operator NullableInt (int? nullable)
		{
			return nullable.HasValue ? new NullableInt (nullable.Value) : default;
		}

		//methods
		public override bool Equals (object obj)
		{
			if (!hasValue)
			{
				return obj == null;
			}
			if (obj == null)
			{
				return false;
			}
			return value.Equals (obj);
		}

		public override int GetHashCode ()
		{
			if (!hasValue)
			{
				return 0;
			}
			return value.GetHashCode ();
		}

		public override string ToString ()
		{
			if (!hasValue)
			{
				return string.Empty;
			}
			return value.ToString ();
		}

		public int GetValueOrDefault ()
		{
			return hasValue ? value : default;
		}

		public int GetValueOrDefault (int defaultValue)
		{
			return hasValue ? value : defaultValue;
		}
	}

	[Serializable]
	public struct NullableFloat
	{
		[SerializeField] bool hasValue;
		[SerializeField] float value;

		public bool HasValue => hasValue;
		public float Value
		{
			get
			{
				if (hasValue)
				{
					return value;
				}
				throw new InvalidOperationException ("Value is null");
			}
		}

		//ctor
		public NullableFloat (float value)
		{
			hasValue = true;
			this.value = value;
		}

		//conversion
		public static implicit operator NullableFloat (float value)
		{
			return new NullableFloat (value);
		}

		public static explicit operator float (NullableFloat nullable)
		{
			return nullable.Value;
		}

		public static implicit operator float? (NullableFloat nullable)
		{
			return nullable.hasValue ? new float? (nullable.value) : default;
		}

		public static implicit operator NullableFloat (float? nullable)
		{
			return nullable.HasValue ? new NullableFloat (nullable.Value) : default;
		}

		//methods
		public override bool Equals (object obj)
		{
			if (!hasValue)
			{
				return obj == null;
			}
			if (obj == null)
			{
				return false;
			}
			return value.Equals (obj);
		}

		public override int GetHashCode ()
		{
			if (!hasValue)
			{
				return 0;
			}
			return value.GetHashCode ();
		}

		public override string ToString ()
		{
			if (!hasValue)
			{
				return string.Empty;
			}
			return value.ToString ();
		}

		public float GetValueOrDefault ()
		{
			return hasValue ? value : default;
		}

		public float GetValueOrDefault (float defaultValue)
		{
			return hasValue ? value : defaultValue;
		}
	}

	[Serializable]
	public struct NullableBool
	{
		[SerializeField] bool hasValue;
		[SerializeField] bool value;

		public bool HasValue => hasValue;
		public bool Value
		{
			get
			{
				if (hasValue)
				{
					return value;
				}
				throw new InvalidOperationException ("Value is null");
			}
		}

		//ctor
		public NullableBool (bool value)
		{
			hasValue = true;
			this.value = value;
		}

		//conversion
		public static implicit operator NullableBool (bool value)
		{
			return new NullableBool (value);
		}

		public static explicit operator bool (NullableBool nullable)
		{
			return nullable.Value;
		}

		public static implicit operator bool? (NullableBool nullable)
		{
			return nullable.hasValue ? new bool? (nullable.value) : default;
		}

		public static implicit operator NullableBool (bool? nullable)
		{
			return nullable.HasValue ? new NullableBool (nullable.Value) : default;
		}

		//methods
		public override bool Equals (object obj)
		{
			if (!hasValue)
			{
				return obj == null;
			}
			if (obj == null)
			{
				return false;
			}
			return value.Equals (obj);
		}

		public override int GetHashCode ()
		{
			if (!hasValue)
			{
				return 0;
			}
			return value.GetHashCode ();
		}

		public override string ToString ()
		{
			if (!hasValue)
			{
				return string.Empty;
			}
			return value.ToString ();
		}

		public bool GetValueOrDefault ()
		{
			return hasValue ? value : default;
		}

		public bool GetValueOrDefault (bool defaultValue)
		{
			return hasValue ? value : defaultValue;
		}
	}
}